Implement x86 continuable domain destroy.
authorkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Fri, 31 Aug 2007 16:00:11 +0000 (17:00 +0100)
committerkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Fri, 31 Aug 2007 16:00:11 +0000 (17:00 +0100)
This patch addresses the following bug report.
http://bugzilla.xensource.com/bugzilla/show_bug.cgi?id=1037
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
xen/arch/x86/domain.c
xen/arch/x86/domctl.c
xen/common/domain.c
xen/include/asm-x86/domain.h

index 203ca8ba2a663abdfc6f60cbf0d70155101a1f86..f4f6ca47dfd16074b3bc4466787eba9f79c8aa3d 100644 (file)
@@ -437,6 +437,9 @@ int arch_domain_create(struct domain *d)
     int vcpuid, pdpt_order, paging_initialised = 0;
     int rc = -ENOMEM;
 
+    d->arch.relmem = RELMEM_not_started;
+    INIT_LIST_HEAD(&d->arch.relmem_list);
+
     pdpt_order = get_order_from_bytes(PDPT_L1_ENTRIES * sizeof(l1_pgentry_t));
     d->arch.mm_perdomain_pt = alloc_xenheap_pages(pdpt_order);
     if ( d->arch.mm_perdomain_pt == NULL )
@@ -1599,12 +1602,13 @@ int hypercall_xlat_continuation(unsigned int *id, unsigned int mask, ...)
 }
 #endif
 
-static void relinquish_memory(struct domain *d, struct list_head *list,
-                              unsigned long type)
+static int relinquish_memory(
+    struct domain *d, struct list_head *list, unsigned long type)
 {
     struct list_head *ent;
     struct page_info  *page;
     unsigned long     x, y;
+    int               ret = 0;
 
     /* Use a recursive lock, as we may enter 'free_domheap_page'. */
     spin_lock_recursive(&d->page_alloc_lock);
@@ -1619,6 +1623,7 @@ static void relinquish_memory(struct domain *d, struct list_head *list,
         {
             /* Couldn't get a reference -- someone is freeing this page. */
             ent = ent->next;
+            list_move_tail(&page->list, &d->arch.relmem_list);
             continue;
         }
 
@@ -1653,10 +1658,21 @@ static void relinquish_memory(struct domain *d, struct list_head *list,
 
         /* Follow the list chain and /then/ potentially free the page. */
         ent = ent->next;
+        list_move_tail(&page->list, &d->arch.relmem_list);
         put_page(page);
+
+        if ( hypercall_preempt_check() )
+        {
+            ret = -EAGAIN;
+            goto out;
+        }
     }
 
+    list_splice_init(&d->arch.relmem_list, list);
+
+ out:
     spin_unlock_recursive(&d->page_alloc_lock);
+    return ret;
 }
 
 static void vcpu_destroy_pagetables(struct vcpu *v)
@@ -1719,35 +1735,81 @@ static void vcpu_destroy_pagetables(struct vcpu *v)
 
 int domain_relinquish_resources(struct domain *d)
 {
+    int ret;
     struct vcpu *v;
 
     BUG_ON(!cpus_empty(d->domain_dirty_cpumask));
 
-    /* Tear down paging-assistance stuff. */
-    paging_teardown(d);
+    switch ( d->arch.relmem )
+    {
+    case RELMEM_not_started:
+        /* Tear down paging-assistance stuff. */
+        paging_teardown(d);
 
-    /* Drop the in-use references to page-table bases. */
-    for_each_vcpu ( d, v )
-        vcpu_destroy_pagetables(v);
+        /* Drop the in-use references to page-table bases. */
+        for_each_vcpu ( d, v )
+            vcpu_destroy_pagetables(v);
 
-    /*
-     * Relinquish GDT mappings. No need for explicit unmapping of the LDT as
-     * it automatically gets squashed when the guest's mappings go away.
-     */
-    for_each_vcpu(d, v)
-        destroy_gdt(v);
+        /*
+         * Relinquish GDT mappings. No need for explicit unmapping of the LDT
+         * as it automatically gets squashed when the guest's mappings go away.
+         */
+        for_each_vcpu(d, v)
+            destroy_gdt(v);
+
+        d->arch.relmem = RELMEM_xen_l4;
+        /* fallthrough */
 
-    /* Relinquish every page of memory. */
+        /* Relinquish every page of memory. */
 #if CONFIG_PAGING_LEVELS >= 4
-    relinquish_memory(d, &d->xenpage_list, PGT_l4_page_table);
-    relinquish_memory(d, &d->page_list, PGT_l4_page_table);
+    case RELMEM_xen_l4:
+        ret = relinquish_memory(d, &d->xenpage_list, PGT_l4_page_table);
+        if ( ret )
+            return ret;
+        d->arch.relmem = RELMEM_dom_l4;
+        /* fallthrough */
+       case RELMEM_dom_l4:
+        ret = relinquish_memory(d, &d->page_list, PGT_l4_page_table);
+        if ( ret )
+            return ret;
+        d->arch.relmem = RELMEM_xen_l3;
+        /* fallthrough */
 #endif
+
 #if CONFIG_PAGING_LEVELS >= 3
-    relinquish_memory(d, &d->xenpage_list, PGT_l3_page_table);
-    relinquish_memory(d, &d->page_list, PGT_l3_page_table);
+       case RELMEM_xen_l3:
+        ret = relinquish_memory(d, &d->xenpage_list, PGT_l3_page_table);
+        if ( ret )
+            return ret;
+        d->arch.relmem = RELMEM_dom_l3;
+        /* fallthrough */
+       case RELMEM_dom_l3:
+        ret = relinquish_memory(d, &d->page_list, PGT_l3_page_table);
+        if ( ret )
+            return ret;
+        d->arch.relmem = RELMEM_xen_l2;
+        /* fallthrough */
 #endif
-    relinquish_memory(d, &d->xenpage_list, PGT_l2_page_table);
-    relinquish_memory(d, &d->page_list, PGT_l2_page_table);
+
+       case RELMEM_xen_l2:
+        ret = relinquish_memory(d, &d->xenpage_list, PGT_l2_page_table);
+        if ( ret )
+            return ret;
+        d->arch.relmem = RELMEM_dom_l2;
+        /* fallthrough */
+       case RELMEM_dom_l2:
+        ret = relinquish_memory(d, &d->page_list, PGT_l2_page_table);
+        if ( ret )
+            return ret;
+        d->arch.relmem = RELMEM_done;
+        /* fallthrough */
+
+       case RELMEM_done:
+        break;
+
+    default:
+        BUG();
+    }
 
     /* Free page used by xen oprofile buffer. */
     free_xenoprof_pages(d);
index 40871491ae84534e00703234990226c99a5da096..3245a42bca7102d4b276ec2ab6982c59491dbcdf 100644 (file)
@@ -257,10 +257,14 @@ long arch_do_domctl(
                 break;
             }
 
-            ret = 0;
-
             spin_lock(&d->page_alloc_lock);
 
+            if ( unlikely(d->is_dying) ) {
+                spin_unlock(&d->page_alloc_lock);
+                goto getmemlist_out;
+            }
+
+            ret = 0;
             list_ent = d->page_list.next;
             for ( i = 0; (i < max_pfns) && (list_ent != &d->page_list); i++ )
             {
@@ -279,7 +283,7 @@ long arch_do_domctl(
 
             domctl->u.getmemlist.num_pfns = i;
             copy_to_guest(u_domctl, domctl, 1);
-
+        getmemlist_out:
             rcu_unlock_domain(d);
         }
     }
index a470cc8e73ab6aac489247283a63b5ecedb4dfcc..e3b583d0215f3dce2ed05fe79415932101b3bec9 100644 (file)
@@ -318,6 +318,7 @@ int domain_kill(struct domain *d)
         d->is_dying = DOMDYING_dying;
         evtchn_destroy(d);
         gnttab_release_mappings(d);
+        /* fallthrough */
     case DOMDYING_dying:
         rc = domain_relinquish_resources(d);
         page_scrub_kick();
@@ -329,6 +330,7 @@ int domain_kill(struct domain *d)
         d->is_dying = DOMDYING_dead;
         put_domain(d);
         send_guest_global_virq(dom0, VIRQ_DOM_EXC);
+        /* fallthrough */
     case DOMDYING_dead:
         break;
     }
index 9e7659583dfcc8ca67f27d20357cfdfd3f6b42aa..a913d43529a9729ce18c08a1800064cba0e1a7c0 100644 (file)
@@ -234,6 +234,19 @@ struct arch_domain
     bool_t is_32bit_pv;
     /* Is shared-info page in 32-bit format? */
     bool_t has_32bit_shinfo;
+
+    /* Continuable domain_relinquish_resources(). */
+    enum {
+        RELMEM_not_started,
+        RELMEM_xen_l4,
+        RELMEM_dom_l4,
+        RELMEM_xen_l3,
+        RELMEM_dom_l3,
+        RELMEM_xen_l2,
+        RELMEM_dom_l2,
+        RELMEM_done,
+    } relmem;
+    struct list_head relmem_list;
 } __cacheline_aligned;
 
 #ifdef CONFIG_X86_PAE